﻿using System;
using System.Windows.Forms;
//using PluginInterface;
using System.Drawing;
using Qurihara;
using System.Collections;
using System.Xml;
using Qurihara.Anm;
using System.IO;
using OSC.NET;
using System.Threading;
using System.Net;
using Qurihara.QBasic;
using System.Text;
using System.Collections.Generic;
using Microsoft.Ink;
using KotodamaPlugin;

using System.Configuration;

namespace Qurihara.OSCCom
{
    public class ComForAnalyzer
    {

        #region event for manager
        //event for manager
        public event ViewMovedReceivedEventHandler ViewMovedReceived = null;
        public delegate void ViewMovedReceivedEventHandler(object sender, ViewMovedReceivedEventArgs e);
        public class ViewMovedReceivedEventArgs : EventArgs
        {
            public ViewMovedReceivedEventArgs(string name, Rectangle rect)
            {
                Name = name;
                ViewRectangle = rect;
            }
            public string Name;
            public Rectangle ViewRectangle;
        }
        public event StrokeElementListCreatedEventHandler StrokeElementListCreated = null;
        public delegate void StrokeElementListCreatedEventHandler(object sender, StrokeElementListCreatedEventArgs e);
        public class StrokeElementListCreatedEventArgs : EventArgs
        {
            public StrokeElementListCreatedEventArgs(List<StrokeElement> sList)
            {
                StrokeElementList = sList;
            }
            public List<StrokeElement> StrokeElementList;
        }
        public event NewPersonalInkNameReceivedEventHandler NewPersonalInkNameReveived = null;
        public delegate void NewPersonalInkNameReceivedEventHandler(object sender, NewPersonalInkNameReceivedEventArgs e);
        public class NewPersonalInkNameReceivedEventArgs : EventArgs
        {
            public NewPersonalInkNameReceivedEventArgs(string name)
            {
                Name = name;
            }
            public string Name;
        }

        #endregion

        private Dictionary<FigureElement, DateTime> objectTimestampTable;
        private Dictionary<FigureElement, string> objectNameTable;
        private List<FigureElement> logObjectList;

        public static int InkDumpingFactor = 2;

        protected IKotodamaBase kotodama;

        private object stackedView = null;

        private static Mutex mutInvalidated = new Mutex();
        private ViewSnapList viewSnapList = null;

        private bool enableAutoScroll = false;
        private int snapOffset = 0;


        public string Name
        {
            get { return iComCore.Name; }
            set { iComCore.Name = value; }
        }
        public string Ip
        {
            get { return iComCore.Ip; }
        }
        public int MPort
        {
            get { return iComCore.MPort; }
        }

        protected IComCore iComCore;

        public ComForAnalyzer(IKotodamaBase _kotodama,IComCore iComCore)
            : base()
        {
            objectTimestampTable = new Dictionary<FigureElement, DateTime>();
            objectNameTable = new Dictionary<FigureElement, string>();
            logObjectList = new List<FigureElement>();

            kotodama = _kotodama;
            this.iComCore = iComCore;
            iComCore.MessageReceived += new MessageEventHandler(iComCore_MessageReceived);

            //sendView = Properties.Settings.Default.SendView;
            //canChangeAutoScroll = Properties.Settings.Default.CanChangeAutoScroll;
            //sendInkImmediately = Properties.Settings.Default.SendInkImmediately;
            //sendToAllInk = Properties.Settings.Default.SendToAllInk;
            //sendPersonalInk = Properties.Settings.Default.SendPersonalInk;
            //receivePersonalInk = Properties.Settings.Default.ReceivePersonalInk;

            //snapOffset = Properties.Settings.Default.SnapOffset;
            //overview = Properties.Settings.Default.Overview;
            //sendLog = Properties.Settings.Default.SendLog;
            //manager = Properties.Settings.Default.Manager;
        }

        #region Configuration
        public void Initialize()
        {
            iComCore.Initialize();

            ////MultiViewSnapPluginのよみこみ。このPluginのほうがMultiViewSnapPluginよりも後にinitializeされることが前提。
            //KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin plugin = (KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin)kotodama.EnvironmentTable["MultiViewSnap Plugin"];
            //plugin.SnapChanged += new KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin.SnapChangedEventHandler(plugin_SnapChanged);

            //KotodamaPlugin.ViewWritePlugin.ViewWritePlugin vwp = (KotodamaPlugin.ViewWritePlugin.ViewWritePlugin)kotodama.EnvironmentTable["ViewWrite Plugin"];
            //vwp.ViewWriteMode.StrokeElementCreated += new KotodamaPlugin.ViewWritePlugin.ViewWriteMode.StrokeElementCreatedEventHandler(ViewWriteMode_StrokeElementCreated);

        }

        private void UpdateViewSnapList()
        {
            viewSnapList = (ViewSnapList)kotodama.Presentation.CurrentSlide.ViewSnapListTable[iComCore.Name];
            if (viewSnapList == null && kotodama.Presentation.CurrentSlide.ViewSnapListTable.Count > 0)
            {
                IEnumerator enumes = kotodama.Presentation.CurrentSlide.ViewSnapListTable.Values.GetEnumerator();
                enumes.MoveNext();
                viewSnapList = (ViewSnapList)enumes.Current;
            }
        }
        public void Dispose()
        {
            //KotodamaPlugin.ViewWritePlugin.ViewWritePlugin vwp = (KotodamaPlugin.ViewWritePlugin.ViewWritePlugin)kotodama.EnvironmentTable["ViewWrite Plugin"];
            //vwp.ViewWriteMode.StrokeElementCreated -= new KotodamaPlugin.ViewWritePlugin.ViewWriteMode.StrokeElementCreatedEventHandler(ViewWriteMode_StrokeElementCreated);

            //KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin plugin = (KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin)kotodama.EnvironmentTable["MultiViewSnap Plugin"];
            //plugin.SnapChanged -= new KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin.SnapChangedEventHandler(plugin_SnapChanged);
            
            //kotodama.Presentation.CurrentSlide.View.ViewChanged -= new EventHandler(View_ViewChanged);
            iComCore.Dispose();
        }
        #endregion

        private List<Rectangle> RectangleDeserialize(string src)
        {
            List<Rectangle> outList = new List<Rectangle>();
            if (src == "") return outList;
            string[] recs = src.Split('*');
            foreach (string rec in recs)
            {
                string[] d = rec.Split(':');
                int x = int.Parse(d[0]);
                int y = int.Parse(d[1]);
                int w = int.Parse(d[2]);
                int h = int.Parse(d[3]);
                Rectangle r = new Rectangle(x, y, w, h);
                outList.Add(r);
            }
            return outList;
        }

        #region Receiving

        void iComCore_MessageReceived(object o, MessageEventArgs e)
        {
            Delegate d = (MethodInvoker)delegate()
            {
                string adr = e.Adr;
                string data = e.Data;

                if (adr.Equals("/load"))
                {
                    string[] dat = data.Split(',');
                    string kotodamaWebServerIP = dat[0];
                    string filename = dat[1];

                    KotodamaPlugin.WebIOPlugin.WebIOPlugin plugin = (KotodamaPlugin.WebIOPlugin.WebIOPlugin)kotodama.EnvironmentTable["WebIO Plugin"];

                    //以下の１と２はどちらも同じ。
                    //1
                    plugin.LinkBaseDir = "";
                    plugin.LoadXmlWeb = filename;
                    //2
                    //plugin.LoadFile(filename);

                    //ここでプレイリスト名を自分の名前のものにする！
                    UpdateViewSnapList();
                    if (viewSnapList != null && viewSnapList.Count > 0)
                    {
                        //PresentationのCurrentSnapChangedがなくなって、
                        //Slideで扱えるようになって、
                        //MultiViewSnapPluginがkotodamaPanelTestでも
                        //ロード時にちゃんとリストを更新できるようになるまでの措置
                        if (kotodama.EnvironmentTable.Contains("CurrentViewSnapList"))
                        {
                            kotodama.EnvironmentTable.Remove("CurrentViewSnapList");
                        }
                        kotodama.EnvironmentTable.Add("CurrentViewSnapList", viewSnapList);

                        ((ViewSnap)viewSnapList[0]).Warp();
                    }


                    InitPersonalInkSystem();

                    kotodama.Control.Invalidate();

                }

                if (adr.Equals("/draw_strokes"))
                {
                    string[] dat = data.Split(',');
                    //CreateStrokes(dat[0], "_all_");
                    string recText = "";
                    if (dat.Length > 1) recText = dat[1];
                     List<StrokeElement> fel = CreateStrokes(dat[0], recText, "");

                    //add for logexport **************************************
                     foreach (StrokeElement se in fel)
                     {
                         AddLog(se, e.SenderName, e.TimeStamp);
                     }

                }

                if (adr.Equals("/private__strokes"))
                {
                    string[] dat = data.Split(',');
                    string sender = dat[0];
                    string recText = "";
                    if (dat.Length > 2) recText = dat[2];
                    List<StrokeElement> fel = CreateStrokes(dat[1], recText, sender);
                    HideElements(fel);
                    UpdatePersonalInk();

                    //add for logexport **************************************
                    foreach (StrokeElement se in fel)
                    {
                        AddLog(se, e.SenderName, e.TimeStamp);
                    }
                }
                if (adr.Equals("/view__report"))
                {
                    string[] dat = data.Split(',');
                    Rectangle view = new Rectangle(
                        int.Parse(dat[0]),
                        int.Parse(dat[1]),
                        int.Parse(dat[2]),
                        int.Parse(dat[3]));

                    ///HSB空間から色を指定する．hue:0 to 360, saturation: 0 to 1, brightness: 0 to 1
                    //Color c = QBasic.DrawingUtil.ColorFromHSB(1, 1, 1);
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createRectangle(-1,view,Color.Transparent,Color.Blue,1);
                    fe.Visible = false;
                    //createdStrokeElementList.Add((StrokeElement)fe);
                    kotodama.Control.Invalidate();

                    //add for logexport **************************************
                    AddLog(fe, e.SenderName, e.TimeStamp);
                }
                if (adr.Equals("/set_view"))
                {
                    string[] dat = data.Split(',');
                    Rectangle destRect = new Rectangle(int.Parse(dat[0]), int.Parse(dat[1]), int.Parse(dat[2]), int.Parse(dat[3]));

                    ///HSB空間から色を指定する．hue:0 to 360, saturation: 0 to 1, brightness: 0 to 1
                    //Color c = QBasic.DrawingUtil.ColorFromHSB(1, 1, 1);
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createRectangle(-1, destRect, Color.Transparent, Color.Red, 1);
                    fe.Visible = false;
                    kotodama.Control.Invalidate();

                    //add for logexport **************************************
                    AddLog(fe, e.SenderName, e.TimeStamp);
                }
                //if (adr.Equals("/go__snap"))
                //{
                //    string[] dat = data.Split(',');
                //    int snapID = int.Parse(dat[0]);
                //    //GoSnap(snapID);
                //}

            };
            kotodama.Control.BeginInvoke(d);
        }

#region personalInk
        private void UpdatePersonalInk()
        {
            foreach (string name in showingInkList)
            {
                List<StrokeElement> list =
                    GetStrokeElementListByName(name);
                if (list.Count > 0)
                {
                    ShowElements(list);
                    kotodama.Control.Invalidate();
                }
            }
        }
        private List<string> showingInkList = new List<string>();
        private List<StrokeElement> GetStrokeElementListByName(string name)
        {
            List<StrokeElement> list = new List<StrokeElement>();
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                if (fe is StrokeElement)
                {
                    StrokeElement se = fe as StrokeElement;
                    if (se.Tag.Equals("owner="+name))
                    {
                        list.Add(se);
                    }

                }
            }
            return list;
        }
        private List<StrokeElement> GetAllStrokeElementList()
        {
            List<StrokeElement> list = new List<StrokeElement>();
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                if (fe is StrokeElement)
                {
                    StrokeElement se = fe as StrokeElement;
                    if (se.Tag.StartsWith("owner="))
                    {
                        list.Add(se);
                    }

                }
            }
            return list;

        }
        //private List<StrokeElement> allList = new List<StrokeElement>();
        private List<string> personalInkNameList = new List<string>();
        private void InitPersonalInkSystem()
        {
            personalInkNameList.Clear();
            showingInkList.Clear();

        }

        private static void HideElements(List<StrokeElement> fel)
        {
            foreach (StrokeElement se in fel)
            {
                se.Findable = false;
                se.Visible = false;
                //se.Editable = false;
            }
        }
        private static void ShowElements(List<StrokeElement> fel)
        {
            foreach (StrokeElement se in fel)
            {
                se.Findable = true;
                se.Visible = true;
                //se.Editable = true;
            }
        }
        private List<StrokeElement> createdStrokeElementList = new List<StrokeElement>();

        private List<StrokeElement> CreateStrokes(string srcInk, string srcRect,string owner)
        {
            createdStrokeElementList.Clear();
            while (kotodama.InkCollector.CollectingInk) { }
            List<Stroke> list = InkUtil.InkDeserialize(kotodama.InkCollector.Ink, srcInk);
            List<Rectangle> rList = RectangleDeserialize(srcRect);

            if (list.Count == rList.Count)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    Stroke s = list[i];
                    Rectangle re = rList[i];
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createStroke(-1, re, s);
                    ((StrokeElement)fe).SynchronizeBoundingBox();
                    if (owner != "")
                    {
                        fe.Tag = "owner=" + owner;
                        if (!personalInkNameList.Contains(owner))
                        {
                            personalInkNameList.Add(owner);
                            if (NewPersonalInkNameReveived != null) NewPersonalInkNameReveived(this, new NewPersonalInkNameReceivedEventArgs(owner));
                        }
                    }
                    createdStrokeElementList.Add((StrokeElement)fe);
                    kotodama.Control.Invalidate();

                }
            }
            else
            {
                foreach (Stroke stroke in list)
                {
                    Rectangle re;
                    re = new Rectangle(stroke.GetBoundingBox().Location, stroke.GetBoundingBox().Size);
                    InkUtil.GetBoundingBoxFromInk(kotodama.Control, ref re, kotodama.InkCollector);
                    //InkUtil.GetViewBoundingBoxFromInk(kotodama.Control, ref re, kotodama.InkCollector,kotodama.Presentation.CurrentSlide.View);
                    Stroke s = stroke;
                    //kotodama.Presentation.CurrentSlide.View.InkScreenToInkView(ref s);
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createStroke(-1, re, s);
                    ((StrokeElement)fe).SynchronizeBoundingBox();
                    if (owner != "")
                        fe.Tag = "owner=" + owner;
                    createdStrokeElementList.Add((StrokeElement)fe);
                    //fe.Invalidate();
                    //Host.Kotodama.Control.Invalidate(fe.BoundingBox);
                    kotodama.Control.Invalidate();
                }
            }
            if (createdStrokeElementList.Count > 0 && StrokeElementListCreated != null)
                StrokeElementListCreated(this,new StrokeElementListCreatedEventArgs(createdStrokeElementList));
            return createdStrokeElementList;
        }
#endregion

        #endregion

        //外部呼出し用。資料をexportする
        void AddLog(FigureElement fe, string sender, DateTime datetime)
        {
            logObjectList.Add(fe);
            objectTimestampTable.Add(fe, datetime);
            objectNameTable.Add(fe, sender);
        }
        public void Export(Stream viewSnapStream, Stream timeLogStream)
        {
            using (XmlTextWriter xtw = new XmlTextWriter(viewSnapStream,Encoding.UTF8))
            {
                if (viewSnapList != null && viewSnapList.Count > 0)
                {
                    //viewsnapをexport
                    //((ViewSnapList)viewSnapList[0]).Save(xtw);
                    ((ViewSnapList)viewSnapList).Save(xtw);
                }
            }
            using (StreamWriter sw = new StreamWriter(timeLogStream))
            {
                //各ユーザごとにexport
                //タイムスタンプつきviewrectangle,
                //かきこみboundingbox

                foreach (FigureElement fe in logObjectList)
                {
                    DateTime time = objectTimestampTable[fe];
                    string sender = objectNameTable[fe];

                    sw.Write(time);
                    sw.Write(",");
                    sw.Write(sender);
                    sw.Write(",");
                    if (fe is StrokeElement)
                    {
                        sw.Write("stroke,");
                    }
                    else if (fe is RectangleElement)
                    {
                        RectangleElement re = fe as RectangleElement;
                        if (re.OutLineColor == Color.Blue)
                        {
                            sw.Write("view,");
                        }
                        else
                        {
                            sw.Write("set_view,");
                        }
                    }
                    Rectangle r = fe.BoundingBox;
                    sw.Write(r.X);
                    sw.Write(",");
                    sw.Write(r.Y);
                    sw.Write(",");
                    sw.Write(r.Width);
                    sw.Write(",");
                    sw.Write(r.Height);
                    sw.Write(sw.NewLine);
                }
                sw.Close();
            }
        }
    }
}
